/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package opensocial.apps.common;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import opensocial.apps.common.command.ResponseItem;
import opensocial.apps.common.config.ConfigFactory;
import opensocial.apps.common.tools.BeanJsonConverter;

import org.apache.commons.chain.Catalog;
import org.apache.commons.chain.Command;
import org.apache.log4j.Logger;

/**
 * Dispatches the request to corresponding Command according to the
 * configuration of that App on that container.
 * 
 */
public class AppController extends HttpServlet {
    private static final long serialVersionUID = 490822860642753030L;
    private static Logger LOGGER = Logger.getLogger(AppController.class);

    private static BeanJsonConverter converter = new BeanJsonConverter();

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
        handle(req, resp);

    }

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
        handle(req, resp);
    }

    private void handle(HttpServletRequest req, HttpServletResponse resp) {
        try {

            req.setCharacterEncoding("UTF-8");
            Map<String, String> authresult = authenticate(req);
            String container = authresult.get(AppContext.KEY_CONTAINER);// req.getParameter(AppContext.KEY_CONTAINER);
            if (!ConfigFactory.isValidContainerId(container)) {
                throw new IllegalArgumentException("Parameter error: container Id '"
                        + container + "' is invalid.");
            }

            String cmd = req.getParameter(AppContext.KEY_COMMAND);
            if (cmd == null && cmd.trim().length() == 0) {
                throw new IllegalArgumentException("Parameter error: commId '" + cmd
                        + "' is invalid.");
            }

            CatalogFactory catalogfactory = CatalogFactory.getCatalogFactory();
            Catalog catalog = catalogfactory.getCatalog(container);
            if (catalog == null) {
                throw new IllegalArgumentException(
                "Parameter error: catalog is invalid.");
            }

            Command command = catalog.getCommand(cmd);
            if (command == null) {
                throw new IllegalArgumentException("Parameter error: commId '" + cmd
                        + "' is invalid.");
            }
            ConfigFactory factory = ConfigFactory.getConfigFactory();
            AppContext context = new AppContext(factory.getContainerMeta(container),
                    factory.getConfiguration(container), getServletContext(), req, resp);

            context.put(AppContext.KEY_CONTAINER, container);
            context.put(AppContext.KEY_OWNER_ID, authresult
                    .get(AppContext.KEY_OWNER_ID));
            context.put(AppContext.KEY_VIEWER_ID, authresult
                    .get(AppContext.KEY_VIEWER_ID));

            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Command '" + cmd + "' on container '" + container
                        + "' is going to execute.");
            }
            command.execute(context);
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("Successfully executed command '" + cmd
                        + "' on container '" + container + "'.");
            }

            if (context.get(AppContext.OUTPUT_KEY) != null) {
                resp.setContentType("text/html");
                resp.setCharacterEncoding("UTF-8");
                PrintWriter out = resp.getWriter();
                String output = converter.convertToString(context
                        .get(AppContext.OUTPUT_KEY));
                out.print(output);
                out.flush();
                out.close();
            }

        } catch (AppException e) {
            LOGGER.debug("Failed to execute request.", e);
            responseError(resp,e);
        }catch(Exception e){
            LOGGER.error("Failed to execute request.", e);
        }
    }

    // dummy authenticate for test
    private Map<String, String> authenticate(HttpServletRequest req)
    throws Exception {
        Map<String, String> ret = new HashMap<String, String>();

        ret.put(AppContext.KEY_VIEWER_ID, req
                .getParameter(AppContext.KEY_VIEWER_ID));
        ret.put(AppContext.KEY_OWNER_ID, req.getParameter(AppContext.KEY_OWNER_ID));
        ret.put(AppContext.KEY_CONTAINER, req
                .getParameter(AppContext.KEY_CONTAINER));

        return ret;
    }

    private void responseError(HttpServletResponse resp,AppException error) {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("UTF-8");
        PrintWriter out;
        try {
            out = resp.getWriter();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        ResponseItem ri = new ResponseItem();
        ri.setHasError(true);
        ri.set("message", error.getErrorMessage());
        out.print(converter.convertToString(ri.getData()));
        out.flush();
        out.close();
    }
}
